﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;
using System.Collections;
using System.Reflection;
using System.Globalization;
using System.Collections.Concurrent;

namespace RAP.Framework.Libary.Utils
{
  /// <summary>
  /// 动态哈希表，可通过点域访问属性值
  /// </summary>
  public class DynamicHashtable : DynamicObject, IDictionary
  {
    //继承自DynamicHashtable的类型属性缓存
    private static readonly IDictionary<Type, IDictionary<string, PropertyInfo>> _dynamicHashtablePropertyCaches = new ConcurrentDictionary<Type, IDictionary<string, PropertyInfo>>();

    //忽略DynamicHashtable自带的属性
    private static readonly HashSet<string> _invalidProperties = typeof(DynamicHashtable).GetProperties().ToHashSet(p => p.Name);

    //内部Hashtable
    private IDictionary _data;

    //自身属性缓存
    private IDictionary<string, PropertyInfo> _propertyCache = null;

    /// <summary>
    /// 
    /// </summary>
    public bool AutoCreateProperty { get; set; } = true;

    /// <summary>
    /// 
    /// </summary>
    public DynamicHashtable()
      : this(null)
    {

    }

    /// <summary>
    /// 
    /// </summary>
    protected virtual IDictionary<string, PropertyInfo> PropertyCache => _propertyCache;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="capacity"></param>
    public DynamicHashtable(int capacity)
      : base()
    {
      _data = new Hashtable(capacity);
      this.Initialize();
    }

    /// <summary>
    /// 
    /// </summary>
    public DynamicHashtable(IDictionary d = null)
      : base()
    {
      if (d != null)
      {
        _data = new Hashtable(d);
      }
      else
      {
        _data = new Hashtable();
      }
      this.Initialize();
    }

    private void Initialize()
    {
      var type = this.GetType();
      if (!_dynamicHashtablePropertyCaches.TryGetValue(type, out _propertyCache))
      {
        lock (type)
        {
          if (!_dynamicHashtablePropertyCaches.TryGetValue(type, out _propertyCache))
          {
            _propertyCache = new Dictionary<string, PropertyInfo>();
            foreach (var pInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
              if (_invalidProperties.Contains(pInfo.Name))
              {
                continue;
              }
              var attr = pInfo.GetCustomAttribute<DynamicPropertyAttribute>();
              if (attr != null)
              {
                _propertyCache[attr.Property] = pInfo;
              }
              _propertyCache[pInfo.Name] = pInfo;
            }
            _dynamicHashtablePropertyCaches.Add(type, _propertyCache);
          }
        }
      }
    }

    #region IDictionary接口实现
    /// <summary>
    /// 在对象中添加一个带有所提供的键和值的元素。
    /// </summary>
    /// <param name="key">用作要添加的元素的键。</param>
    /// <param name="value">用作要添加的元素的值。</param>
    public void Add(object key, object value)
    {
      _data.Add(key, value);
    }

    /// <summary>
    /// 从对象中移除所有元素。
    /// </summary>
    public void Clear()
    {
      _data.Clear();
    }

    /// <summary>
    /// 确定对象是否包含具有指定键的元素。
    /// </summary>
    /// <param name="key">要在对象中定位的键。</param>
    /// <returns></returns>
    public bool Contains(object key)
    {
      if (key == null || key.ToString() == string.Empty)
      {
        return false;
      }
      return _propertyCache.ContainsKey(key.ToString()) || _data.Contains(key);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public bool ContainsKey(object key)
    {
      return this.Contains(key);
    }

    /// <summary>
    /// 返回一个用于IDictionary对象的IDictionaryEnumerator对象
    /// </summary>
    /// <returns></returns>
    public IDictionaryEnumerator GetEnumerator()
    {
      return _data.GetEnumerator();
    }

    /// <summary>
    /// 获取一个值，该值指示对象是否具有固定大小。
    /// </summary>
    public bool IsFixedSize
    {
      get { return this._data.IsFixedSize; }
    }

    /// <summary>
    /// 获取一个值，该值指示对象是否为只读。
    /// </summary>
    public bool IsReadOnly
    {
      get { return this._data.IsReadOnly; }
    }

    /// <summary>
    /// 获取对象包含的键集合
    /// </summary>
    public ICollection Keys
    {
      get { return this._data.Keys; }
    }

    /// <summary>
    /// 从对象中移除带有指定键的元素。
    /// </summary>
    /// <param name="key"></param>
    public void Remove(object key)
    {
      this._data.Remove(key);
    }

    /// <summary>
    /// 获取对象包含的值集合。
    /// </summary>
    public ICollection Values
    {
      get
      {
        return this._data.Values;
      }
    }

    /// <summary>
    /// 获取或设置具有指定键的元素。仅仅供对象外部调用
    /// </summary>
    /// <param name="key">要获取或设置的元素的键。</param>
    /// <returns>带有指定键的元素。</returns>
    public object this[object key]
    {
      get
      {
        return this.GetPropertyValue(key.ToString());
      }
      set
      {
        this.SetPropertyValue(key.ToString(), value);
      }
    }

    /// <summary>
    /// 从特定的数组索引处开始，将对象的元素复制到一个数组中
    /// </summary>
    /// <param name="array">目标数组</param>
    /// <param name="index">中从零开始的索引，将在此处开始复制</param>
    public void CopyTo(Array array, int index)
    {
      this._data.CopyTo(array, index);
    }

    /// <summary>
    /// 获取包含的元素数
    /// </summary>
    public int Count
    {
      get { return this._data.Count; }
    }

    /// <summary>
    /// 获取一个值，该值指示对象是否线程安全
    /// </summary>
    public bool IsSynchronized
    {
      get { return this._data.IsSynchronized; }
    }

    /// <summary>
    /// 获取一个可用于对象同步访问的对象。
    /// </summary>
    public object SyncRoot
    {
      get { return this._data.SyncRoot; }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    IEnumerator IEnumerable.GetEnumerator()
    {
      return this._data.GetEnumerator();
    }
    #endregion

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public override IEnumerable<string> GetDynamicMemberNames()
    {
      return this.Keys.Cast<string>();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual object GetPropertyValue(string key)
    {
      //检查属性是否存在
      if (_propertyCache.ContainsKey(key))
      {
        var property = _propertyCache[key];
        if (property.CanRead)
        {
          return _propertyCache[key].GetValue(this, null);
        }
      }
      object result;
      //如果不包含指定键的属性，则创建一个DynamicHastable并存入指定的键
      if (!this._data.Contains(key) && AutoCreateProperty)
      {
        this._data[key] = result = new DynamicHashtable();
      }
      else
      {
        result = this._data[key];
      }
      return result;
    }

    /// <summary>
    /// 设置属性值
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public virtual void SetPropertyValue(string key, object value)
    {
      if (_propertyCache.ContainsKey(key))
      {
        var pInfo = _propertyCache[key];
        if (pInfo.CanWrite)
        {
          pInfo.SetValue(this, value, null);
        }
      }
      this._data[key] = value;
    }

    /// <summary>
    /// 重载基类的TryGetMember实现
    /// </summary>
    /// <param name="binder"></param>
    /// <param name="result"></param>
    /// <returns></returns>
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
      result = this.GetPropertyValue(binder.Name);
      return true;
    }

    /// <summary>
    /// 重载基类的TrySetMember实现
    /// </summary>
    /// <param name="binder"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
      this.SetPropertyValue(binder.Name, value);
      return true;
    }

    /// <summary>
    /// 重载基类的TryInvokeMember实现
    /// </summary>
    /// <param name="binder"></param>
    /// <param name="args"></param>
    /// <param name="result"></param>
    /// <returns></returns>
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
      var method = this.GetType().GetMethod(binder.Name, BindingFlags.Public | BindingFlags.Instance);
      if (method != null)
      {
        result = method.Invoke(this, args);
        return true;
      }

      var fun = this._data[binder.Name] as Delegate;
      result = null;
      if (fun != null)
      {
        result = fun.DynamicInvoke(args);
        return true;
      }
      return false;
    }

    /// <summary>
    /// 对象属性set赋值时必须采用该方法，否则堆栈移除
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public DynamicHashtable Set(object key, object value)
    {
      this._data[key] = value;
      return this;
    }

    /// <summary>
    /// 对象属性get取值时必须采用内部取值方法，否则堆栈移除
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public object Get(object key)
    {
      return this._data[key];
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="target"></param>
    /// <returns></returns>
    public virtual T CopyTo<T>(T target) where T : IDictionary
    {
      (this as IDictionary).CopyTo(target);
      foreach (var property in PropertyCache.Values)
      {
        target[property.Name] = this[property.Name];
      }
      return target;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="src"></param>
    /// <returns></returns>
    public virtual void CopyFrom(IDictionary src)
    {
      if (src == null)
      {
        return;
      }
      foreach (DictionaryEntry ety in src)
      {
        this[ety.Key] = ety.Value;
      }
    }

    #region 扩展函数
    /// <summary>
    /// 对象属性get取值时必须采用内部取值方法，否则堆栈移除
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual string ToString(object key, string defaultValue = null)
    {
      return this._data.ToString(key, defaultValue);
    }

    /// <summary>
    /// 对象属性get取值时必须采用内部取值方法，否则堆栈移除
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual bool ToBoolean(object key, bool defaultValue = false)
    {
      return this._data.ToBoolean(key, defaultValue);
    }

    /// <summary>
    /// 对象属性get取值时必须采用内部取值方法，否则堆栈移除
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual int ToInt32(object key, int defaultValue = 0)
    {
      return this._data.ToInt32(key, defaultValue);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="defaultValue"></param>
    /// <returns></returns>
    public virtual T ToEnum<T>(object key, T defaultValue = default(T))
    {
      return this._data.ToEnumValue<T>(key, defaultValue);
    }

    /// <summary>
    /// 对象属性get取值时必须采用内部取值方法，否则堆栈移除
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual DateTime? ToDateTime(object key, DateTime? defaultValue = null)
    {
      return this._data.ToDateTime(key, defaultValue);
    }

    /// <summary>
    /// 对象属性get取值时必须采用内部取值方法，否则堆栈移除
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual IDictionary ToDictionary(object key, IDictionary defaultValue = null)
    {
      return this._data.ToDictionaryValue(key, (arg1, arg2) => defaultValue);
    }

    /// <summary>
    /// 对象属性get取值时必须采用内部取值方法，否则堆栈移除
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual T ToObject<T>(object key, T defaultValue = default(T))
    {
      return this._data.ToObject<T>(key, defaultValue);
    }

    /// <summary>
    /// 对象属性get取值时必须采用内部取值方法，否则堆栈移除
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual IList ToList(object key, IList defaultValue = null)
    {
      return this._data.ToList(key, defaultValue);
    }
    #endregion
  }
}
